package clicache

import (
	"bytes"
	"encoding/binary"
	"gitee.com/zhongguo168a/gocodes/datax/binaryx"
	"github.com/pkg/errors"
	"strings"
	"sync"
)

// ==============================================================
// ClientCache
//

func NewClientCache() (obj *ClientCache) {
	obj = &ClientCache{
		buckets:      map[string]interface{}{},
		tableCreator: map[string]func() interface{}{},
	}
	return
}

type ClientCache struct {
	//
	buckets map[string]interface{}
	//
	tableCreator map[string]func() interface{}
	//
	mu sync.RWMutex
}

func (c *ClientCache) GetBucket(key string) map[string]interface{} {
	return c.getBucket(strings.Split(key, "/"))
}

func (c *ClientCache) getBucket(keyarr []string) map[string]interface{} {
	cur := c.buckets
	for _, item := range keyarr {
		item = strings.ToLower(item)
		next := cur[item]
		if next == nil {
			next = map[string]interface{}{}
			cur[item] = next
		}
		cur = next.(map[string]interface{})
	}

	return cur
}
func (c *ClientCache) _parseKey(key string) (bucket map[string]interface{}, id string) {
	keyarr := strings.Split(key, "/")
	id = keyarr[len(keyarr)-1]
	keyarr = keyarr[:len(keyarr)-1]
	bucket = c.getBucket(keyarr)
	return
}

func (c *ClientCache) Register(key string, creator func() interface{}) {
	c.mu.Lock()
	defer c.mu.Unlock()

	c.tableCreator[key] = creator
}

func (c *ClientCache) GetData(key string) (r interface{}, has bool) {
	c.mu.RLock()
	defer c.mu.RUnlock()

	bucket, id := c._parseKey(key)
	r, has = bucket[id]
	return
}

func (c *ClientCache) Receive(rb []byte) {
	connReader := bytes.NewReader(rb)
	seq, _ := binaryx.ReadInt16(connReader, binary.BigEndian)
	_ = seq
	seqsrv, _ := binaryx.ReadInt16(connReader, binary.BigEndian)
	for {
		if connReader.Len() == 0 {
			break
		}
		//packetLen := binaryutil.ReadInt16(connReader, binary.BigEndian)
		//if int(packetLen) > connReader.Len() {
		//	fmt.Errorf(`<<< parse bytes error: packet length > bytes available: ${packetLen} > ${connReader.bytesAvailable}\n`)
		//	return
		//}
		data, _ := binaryx.ReadBytes(connReader, binary.BigEndian)
		reader := bytes.NewReader(data)
		opt, _ := binaryx.ReadInt8(reader, binary.BigEndian)
		c.parsePacket(connReader, reader, int(opt), int(seqsrv))
	}

}

func (c *ClientCache) parsePacket(connReader, reader *bytes.Reader, opt int, seqsrv int) {

}

func (c *ClientCache) Clean() {
	c.buckets = map[string]interface{}{}
}

func (c *ClientCache) SetData(key string, obj interface{}) {
	c.mu.Lock()
	defer c.mu.Unlock()

	bucket, id := c._parseKey(key)
	bucket[id] = obj
}

func (c *ClientCache) NewData(key string) (r interface{}, err error) {
	c.mu.Lock()
	defer c.mu.Unlock()

	f, has := c.tableCreator[key]
	if has {
		r = f()
	} else {
		err = errors.New("key creator not found: " + key)
	}

	return
}

func (c *ClientCache) DeleteData(key string) {
	c.mu.Lock()
	defer c.mu.Unlock()

	bucket, id := c._parseKey(key)
	delete(bucket, id)
}
